/* OpenSSL headers */
#include <stdio.h>
#include "openssl/bio.h"
#include "openssl/ssl.h"
#include "openssl/err.h"

// Bugs:
// OpenSSL_add_all_algorithms()和OpenSSL_add_ssl_algorithms()的区别:
// 我们在使用前者的时候，当server启动的时候会出下面的错误, 换成后者则正常.
// 3767:error:140A90A1:SSL routines:SSL_CTX_new:library has no ciphers:ssl_lib.c:1456:


// 一个使用SSL BIO的服务器端的例子
// 依赖:
// 服务器端程序依赖两个文件:
// a. 私钥文件 - privatekey.pem
//    openssl genrsa -out privatekey.pem 2048
// b. 证书文件 - cacert.pem
//    openssl req -new -x509 -key privatekey.pem -out cacert.pem -days 1095
//
// 流程:
// <1> 建立一个SSL_CTX - ctx = SSL_CTX_new(SSLv23_method())
// <2> 加载"服务器端证书"
//     SSL_CTX_use_certificate_file
// <3> 加载"私钥"
//     SSL_CTX_use_PrivateKey_file
// <4> 检查证书和私钥
//     SSL_CTX_check_private_key
// <5> 设置server端的SSL BIO
// <6> 设置accept bio对象
// <7> BIO_do_accept(abio) 
// <8> 使用out = BIO_pop获得新进的bio连接
// <9> 握手BIO_do_handshake(out) 
// <10> 使用BIO_read和BIO_write来接收和发送数据
int main(int argc, char **argv) {
  SSL_CTX *ctx;
  SSL *ssl;
  BIO *ssl_bio, *abio, *out;

  char *wbuf = "this is a s -> c msg";
  char rbuf[128];
  memset(rbuf, 0, 128);

  /* Initializing OpenSSL */
 
  OpenSSL_add_ssl_algorithms();
  SSL_load_error_strings();
  ERR_load_BIO_strings();
  ERR_load_SSL_strings();
  

  ctx = SSL_CTX_new(SSLv2_method());
  if(ctx == NULL) {
    //ERR_print_errors_fp(stdout); // 获取错误信息
    printf("SSL_CTX_new(SSLv23_method()) error\n");
    return -1;
  }

  if(!SSL_CTX_use_certificate_file(ctx, "cacert.pem", SSL_FILETYPE_PEM))
  {
    printf("SSL_CTX_use_certificate_file error\n");
    SSL_CTX_free(ctx);
    return -1;
  }
  if(!SSL_CTX_use_PrivateKey_file(ctx, "privatekey.pem", SSL_FILETYPE_PEM))
  {
    printf("SSL_CTX_use_PrivateKey_file error\n");
    SSL_CTX_free(ctx);
    return -1;
  }

  if(!SSL_CTX_check_private_key(ctx)) {
    printf("SSL_CTX_check_private_key error\n");
    SSL_CTX_free(ctx);
    return -1;
  }

  ssl = SSL_new(ctx);
  ssl_bio = BIO_new_ssl(ctx, 0);
  if(ssl_bio == NULL)
  {
    printf("BIO_new_ssl error\n");
    SSL_CTX_free(ctx);
    return -1;
  }

  abio = BIO_new_accept("4422");
  BIO_set_accept_bios(abio, ssl_bio);


  if(BIO_do_accept(abio) <= 0)
  {
    printf("BIO_do_accept - 1 error\n");
    SSL_CTX_free(ctx);
    BIO_free_all(ssl_bio);
    BIO_free_all(abio);
    return -1;
  }

  if(BIO_do_accept(abio) <= 0)
  {
    printf("BIO_do_accept - 2 error\n");
    SSL_CTX_free(ctx);
    BIO_free_all(ssl_bio);
    BIO_free_all(abio);
    return -1;
  }

  out = BIO_pop(abio);

  if(BIO_do_handshake(out) <= 0)
  {
    printf("BIO_do_handshake error\n"); 
    BIO_free_all(out);
    BIO_free_all(ssl_bio);
    BIO_free_all(abio);
    SSL_CTX_free(ctx);
    return -1;
  }

  if(BIO_read(out, rbuf, 128) <= 0) {
    printf("SSL_read error\n");
    BIO_free_all(out);
    BIO_free_all(abio);
    BIO_free_all(ssl_bio);
    SSL_CTX_free(ctx);
    return -1;
  }

  if(BIO_write(out, wbuf, strlen(wbuf)) <= 0)
  {
    BIO_free_all(out);
    BIO_free_all(abio);
    BIO_free_all(ssl_bio);
    SSL_CTX_free(ctx);
    return -1;
  }

  printf("server# %s\n", rbuf);

  BIO_free_all(out);
  BIO_free_all(ssl_bio);
  BIO_free_all(abio);

  SSL_CTX_free(ctx);
  return 0;
}
